# -*- coding: utf-8 -*-
"""
Python 企业级上下文管理器最佳实践示例

本模块演示了Python上下文管理器的企业级最佳实践，包括:
- 基础上下文管理器和with语句
- 自定义上下文管理器类和函数
- 异步上下文管理器
- 资源管理和异常处理
- 嵌套上下文管理器
- 数据库连接和事务管理
- 文件操作和临时资源管理
- 性能监控和日志记录
"""

import asyncio
import contextlib
import functools
import logging
import os
import tempfile
import threading
import time
from contextlib import contextmanager, asynccontextmanager, ExitStack
from dataclasses import dataclass
from enum import Enum, auto
from pathlib import Path
from typing import Any, Dict, List, Optional, Union, Generator, AsyncGenerator
import sqlite3
import weakref

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(levelname)s:%(name)s:%(message)s'
)
logger = logging.getLogger(__name__)

class ResourceType(Enum):
    """资源类型枚举"""
    FILE = auto()
    DATABASE = auto()
    NETWORK = auto()
    MEMORY = auto()
    LOCK = auto()

@dataclass
class ResourceInfo:
    """资源信息"""
    name: str
    resource_type: ResourceType
    created_at: float
    metadata: Optional[Dict[str, Any]] = None

class ResourceManager:
    """资源管理器 - 跟踪和管理资源"""
    
    def __init__(self):
        self._resources: Dict[str, ResourceInfo] = {}
        self._lock = threading.RLock()
    
    def register_resource(self, resource_id: str, resource_info: ResourceInfo) -> None:
        """注册资源"""
        with self._lock:
            self._resources[resource_id] = resource_info
            logger.debug(f"注册资源: {resource_id} ({resource_info.resource_type.name})")
    
    def unregister_resource(self, resource_id: str) -> None:
        """注销资源"""
        with self._lock:
            if resource_id in self._resources:
                resource_info = self._resources.pop(resource_id)
                logger.debug(f"注销资源: {resource_id} ({resource_info.resource_type.name})")
    
    def get_active_resources(self) -> List[ResourceInfo]:
        """获取活跃资源列表"""
        with self._lock:
            return list(self._resources.values())
    
    def cleanup_all(self) -> None:
        """清理所有资源"""
        with self._lock:
            count = len(self._resources)
            self._resources.clear()
            logger.info(f"清理了 {count} 个资源")

# 全局资源管理器
resource_manager = ResourceManager()

class TimingContext:
    """计时上下文管理器类"""
    
    def __init__(self, operation_name: str, log_level: int = logging.INFO):
        self.operation_name = operation_name
        self.log_level = log_level
        self.start_time = 0.0
        self.end_time = 0.0
    
    def __enter__(self):
        self.start_time = time.perf_counter()
        logger.log(self.log_level, f"开始操作: {self.operation_name}")
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.end_time = time.perf_counter()
        duration = self.end_time - self.start_time
        
        if exc_type is None:
            logger.log(self.log_level, f"操作完成: {self.operation_name}，耗时 {duration:.4f} 秒")
        else:
            logger.error(f"操作失败: {self.operation_name}，耗时 {duration:.4f} 秒，错误: {exc_val}")
        
        # 不抑制异常
        return False
    
    @property
    def duration(self) -> float:
        """获取操作持续时间"""
        if self.end_time > 0:
            return self.end_time - self.start_time
        return 0.0

class DatabaseConnection:
    """数据库连接上下文管理器"""
    
    def __init__(self, db_path: str = ":memory:"):
        self.db_path = db_path
        self.connection = None
        self.resource_id = f"db_{id(self)}"
    
    def __enter__(self):
        try:
            self.connection = sqlite3.connect(self.db_path)
            self.connection.row_factory = sqlite3.Row  # 启用字典式访问
            
            # 注册资源
            resource_info = ResourceInfo(
                name=f"SQLite连接: {self.db_path}",
                resource_type=ResourceType.DATABASE,
                created_at=time.time(),
                metadata={"db_path": self.db_path}
            )
            resource_manager.register_resource(self.resource_id, resource_info)
            
            logger.info(f"数据库连接已建立: {self.db_path}")
            return self.connection
            
        except Exception as e:
            logger.error(f"数据库连接失败: {e}")
            raise
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        if self.connection:
            try:
                if exc_type is None:
                    self.connection.commit()
                    logger.debug("数据库事务已提交")
                else:
                    self.connection.rollback()
                    logger.warning(f"数据库事务已回滚，原因: {exc_val}")
            except Exception as e:
                logger.error(f"数据库事务处理失败: {e}")
            finally:
                self.connection.close()
                resource_manager.unregister_resource(self.resource_id)
                logger.info("数据库连接已关闭")
        
        # 不抑制异常
        return False

class ThreadSafeLock:
    """线程安全锁上下文管理器"""
    
    def __init__(self, name: str, timeout: Optional[float] = None):
        self.name = name
        self.timeout = timeout
        self.lock = threading.RLock()
        self.acquired = False
        self.resource_id = f"lock_{id(self)}"
    
    def __enter__(self):
        try:
            # RLock.acquire() 在Python中不支持timeout参数，使用blocking参数
            if self.timeout is None:
                self.acquired = self.lock.acquire(blocking=True)
            else:
                # 对于有超时的情况，使用简单的重试机制
                start_time = time.time()
                while time.time() - start_time < self.timeout:
                    self.acquired = self.lock.acquire(blocking=False)
                    if self.acquired:
                        break
                    time.sleep(0.001)  # 短暂等待
                else:
                    self.acquired = False
            
            if not self.acquired:
                raise TimeoutError(f"获取锁超时: {self.name}")
            
            # 注册资源
            resource_info = ResourceInfo(
                name=f"线程锁: {self.name}",
                resource_type=ResourceType.LOCK,
                created_at=time.time(),
                metadata={"timeout": self.timeout}
            )
            resource_manager.register_resource(self.resource_id, resource_info)
            
            logger.debug(f"获取锁成功: {self.name}")
            return self
            
        except Exception as e:
            logger.error(f"获取锁失败: {self.name}, {e}")
            raise
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        if self.acquired:
            try:
                self.lock.release()
                resource_manager.unregister_resource(self.resource_id)
                logger.debug(f"释放锁成功: {self.name}")
            except Exception as e:
                logger.error(f"释放锁失败: {self.name}, {e}")
        
        return False

@contextmanager
def temporary_file(suffix: str = ".tmp", prefix: str = "temp_", 
                  content: Optional[str] = None) -> Generator[Path, None, None]:
    """临时文件上下文管理器
    
    Args:
        suffix: 文件后缀
        prefix: 文件前缀
        content: 初始内容
    
    Yields:
        临时文件路径
    """
    temp_file = None
    resource_id = None
    
    try:
        # 创建临时文件
        temp_file = tempfile.NamedTemporaryFile(
            mode='w+', suffix=suffix, prefix=prefix, delete=False
        )
        
        # 写入初始内容
        if content:
            temp_file.write(content)
            temp_file.flush()
        
        temp_path = Path(temp_file.name)
        temp_file.close()
        
        # 注册资源
        resource_id = f"temp_file_{id(temp_path)}"
        resource_info = ResourceInfo(
            name=f"临时文件: {temp_path.name}",
            resource_type=ResourceType.FILE,
            created_at=time.time(),
            metadata={"path": str(temp_path), "suffix": suffix}
        )
        resource_manager.register_resource(resource_id, resource_info)
        
        logger.debug(f"创建临时文件: {temp_path}")
        yield temp_path
        
    except Exception as e:
        logger.error(f"临时文件操作失败: {e}")
        raise
    
    finally:
        # 清理临时文件
        if temp_file and not temp_file.closed:
            temp_file.close()
        
        if temp_path and temp_path.exists():
            try:
                temp_path.unlink()
                logger.debug(f"删除临时文件: {temp_path}")
            except Exception as e:
                logger.warning(f"删除临时文件失败: {temp_path}, {e}")
        
        if resource_id:
            resource_manager.unregister_resource(resource_id)

@contextmanager
def change_directory(path: Union[str, Path]) -> Generator[Path, None, None]:
    """临时改变工作目录
    
    Args:
        path: 目标目录路径
    
    Yields:
        新的工作目录路径
    """
    original_cwd = Path.cwd()
    target_path = Path(path)
    
    try:
        if not target_path.exists():
            raise FileNotFoundError(f"目录不存在: {target_path}")
        
        if not target_path.is_dir():
            raise NotADirectoryError(f"路径不是目录: {target_path}")
        
        os.chdir(target_path)
        logger.debug(f"切换工作目录: {original_cwd} -> {target_path}")
        yield target_path
        
    except Exception as e:
        logger.error(f"切换目录失败: {e}")
        raise
    
    finally:
        try:
            os.chdir(original_cwd)
            logger.debug(f"恢复工作目录: {target_path} -> {original_cwd}")
        except Exception as e:
            logger.error(f"恢复工作目录失败: {e}")

@contextmanager
def suppress_exceptions(*exception_types) -> Generator[List[Exception], None, None]:
    """抑制指定类型的异常
    
    Args:
        *exception_types: 要抑制的异常类型
    
    Yields:
        捕获的异常列表
    """
    caught_exceptions = []
    
    try:
        yield caught_exceptions
    except exception_types as e:
        caught_exceptions.append(e)
        logger.warning(f"抑制异常: {type(e).__name__}: {e}")
    except Exception as e:
        logger.error(f"未预期的异常: {type(e).__name__}: {e}")
        raise

class AsyncDatabaseConnection:
    """异步数据库连接上下文管理器"""
    
    def __init__(self, db_path: str = ":memory:"):
        self.db_path = db_path
        self.connection = None
        self.resource_id = f"async_db_{id(self)}"
    
    async def __aenter__(self):
        # 模拟异步数据库连接
        await asyncio.sleep(0.1)  # 模拟连接延迟
        
        try:
            self.connection = sqlite3.connect(self.db_path)
            self.connection.row_factory = sqlite3.Row
            
            # 注册资源
            resource_info = ResourceInfo(
                name=f"异步SQLite连接: {self.db_path}",
                resource_type=ResourceType.DATABASE,
                created_at=time.time(),
                metadata={"db_path": self.db_path, "async": True}
            )
            resource_manager.register_resource(self.resource_id, resource_info)
            
            logger.info(f"异步数据库连接已建立: {self.db_path}")
            return self.connection
            
        except Exception as e:
            logger.error(f"异步数据库连接失败: {e}")
            raise
    
    async def __aexit__(self, exc_type, exc_val, exc_tb):
        if self.connection:
            try:
                if exc_type is None:
                    self.connection.commit()
                    logger.debug("异步数据库事务已提交")
                else:
                    self.connection.rollback()
                    logger.warning(f"异步数据库事务已回滚，原因: {exc_val}")
            except Exception as e:
                logger.error(f"异步数据库事务处理失败: {e}")
            finally:
                self.connection.close()
                resource_manager.unregister_resource(self.resource_id)
                logger.info("异步数据库连接已关闭")
                
                # 模拟异步清理
                await asyncio.sleep(0.05)
        
        return False

@asynccontextmanager
async def async_timing_context(operation_name: str) -> AsyncGenerator[Dict[str, float], None]:
    """异步计时上下文管理器
    
    Args:
        operation_name: 操作名称
    
    Yields:
        包含计时信息的字典
    """
    start_time = time.perf_counter()
    timing_info = {"start_time": start_time, "duration": 0.0}
    
    logger.info(f"开始异步操作: {operation_name}")
    
    try:
        yield timing_info
    except Exception as e:
        logger.error(f"异步操作失败: {operation_name}, {e}")
        raise
    finally:
        end_time = time.perf_counter()
        duration = end_time - start_time
        timing_info["duration"] = duration
        logger.info(f"异步操作完成: {operation_name}，耗时 {duration:.4f} 秒")

def demonstrate_basic_context_managers() -> None:
    """演示基础上下文管理器"""
    print("\n======== 1. 基础上下文管理器 ========")
    
    # 文件操作
    with temporary_file(suffix=".txt", content="Hello, World!") as temp_path:
        print(f"✓ 创建临时文件: {temp_path.name}")
        
        # 读取文件内容
        with open(temp_path, 'r') as f:
            content = f.read()
            print(f"✓ 文件内容: {content}")
    
    print("✓ 临时文件已自动清理")
    
    # 计时上下文
    with TimingContext("数据处理") as timer:
        # 模拟一些工作
        time.sleep(0.1)
        result = sum(range(1000))
    
    print(f"✓ 计算结果: {result}，耗时: {timer.duration:.4f} 秒")

def demonstrate_database_context() -> None:
    """演示数据库上下文管理器"""
    print("\n======== 2. 数据库上下文管理器 ========")
    
    # 成功的数据库操作
    with DatabaseConnection() as conn:
        cursor = conn.cursor()
        
        # 创建表
        cursor.execute("""
            CREATE TABLE users (
                id INTEGER PRIMARY KEY,
                name TEXT NOT NULL,
                email TEXT UNIQUE
            )
        """)
        
        # 插入数据
        users_data = [
            ("Alice", "alice@example.com"),
            ("Bob", "bob@example.com"),
            ("Charlie", "charlie@example.com")
        ]
        
        cursor.executemany(
            "INSERT INTO users (name, email) VALUES (?, ?)",
            users_data
        )
        
        # 查询数据
        cursor.execute("SELECT * FROM users")
        users = cursor.fetchall()
        
        print(f"✓ 成功插入 {len(users_data)} 个用户")
        for user in users:
            print(f"  - {user['name']} ({user['email']})")
    
    print("✓ 数据库连接已自动关闭，事务已提交")
    
    # 失败的数据库操作（演示回滚）
    try:
        with DatabaseConnection() as conn:
            cursor = conn.cursor()
            cursor.execute("CREATE TABLE test (id INTEGER PRIMARY KEY)")
            cursor.execute("INSERT INTO test (id) VALUES (1)")
            
            # 故意引发错误
            raise ValueError("模拟业务逻辑错误")
            
    except ValueError as e:
        print(f"✓ 捕获到预期错误: {e}")
        print("✓ 数据库事务已自动回滚")

def demonstrate_thread_safety() -> None:
    """演示线程安全上下文管理器"""
    print("\n======== 3. 线程安全上下文管理器 ========")
    
    shared_resource = {"counter": 0}
    results = []
    
    def worker(worker_id: int, lock_name: str):
        """工作线程函数"""
        try:
            with ThreadSafeLock(lock_name, timeout=1.0):
                # 模拟临界区操作
                current_value = shared_resource["counter"]
                time.sleep(0.01)  # 模拟处理时间
                shared_resource["counter"] = current_value + 1
                
                result = f"Worker {worker_id}: counter = {shared_resource['counter']}"
                results.append(result)
                
        except Exception as e:
            results.append(f"Worker {worker_id}: 错误 - {e}")
    
    # 创建多个线程
    threads = []
    for i in range(5):
        thread = threading.Thread(target=worker, args=(i, "shared_counter"))
        threads.append(thread)
        thread.start()
    
    # 等待所有线程完成
    for thread in threads:
        thread.join()
    
    print(f"✓ 最终计数器值: {shared_resource['counter']}")
    for result in results:
        print(f"  {result}")

def demonstrate_nested_contexts() -> None:
    """演示嵌套上下文管理器"""
    print("\n======== 4. 嵌套上下文管理器 ========")
    
    # 使用ExitStack管理多个上下文
    with ExitStack() as stack:
        # 添加多个上下文管理器
        timer = stack.enter_context(TimingContext("嵌套操作"))
        temp_file1 = stack.enter_context(temporary_file(suffix=".txt", content="File 1"))
        temp_file2 = stack.enter_context(temporary_file(suffix=".log", content="File 2"))
        lock = stack.enter_context(ThreadSafeLock("nested_lock"))
        
        print(f"✓ 创建临时文件1: {temp_file1.name}")
        print(f"✓ 创建临时文件2: {temp_file2.name}")
        print("✓ 获取线程锁")
        
        # 执行一些操作
        with open(temp_file1, 'a') as f:
            f.write("\nAdditional content")
        
        time.sleep(0.05)  # 模拟处理时间
    
    print(f"✓ 所有资源已清理，总耗时: {timer.duration:.4f} 秒")

def demonstrate_exception_handling() -> None:
    """演示异常处理上下文管理器"""
    print("\n======== 5. 异常处理上下文管理器 ========")
    
    # 抑制特定异常
    with suppress_exceptions(ValueError, TypeError) as caught:
        print("✓ 开始可能出错的操作")
        
        # 这些异常会被抑制
        raise ValueError("这是一个被抑制的错误")
    
    print(f"✓ 捕获到 {len(caught)} 个异常")
    for exc in caught:
        print(f"  - {type(exc).__name__}: {exc}")
    
    # 目录切换
    original_dir = Path.cwd()
    with change_directory("/tmp"):
        current_dir = Path.cwd()
        print(f"✓ 当前目录: {current_dir}")
    
    restored_dir = Path.cwd()
    print(f"✓ 恢复目录: {restored_dir}")
    print(f"✓ 目录切换成功: {original_dir == restored_dir}")

async def demonstrate_async_contexts() -> None:
    """演示异步上下文管理器"""
    print("\n======== 6. 异步上下文管理器 ========")
    
    # 异步计时
    async with async_timing_context("异步数据处理") as timing:
        print("✓ 开始异步操作")
        
        # 模拟异步工作
        await asyncio.sleep(0.1)
        result = sum(range(1000))
        
        print(f"✓ 异步计算结果: {result}")
    
    print(f"✓ 异步操作耗时: {timing['duration']:.4f} 秒")
    
    # 异步数据库操作
    async with AsyncDatabaseConnection() as conn:
        cursor = conn.cursor()
        
        # 创建表并插入数据
        cursor.execute("CREATE TABLE async_test (id INTEGER, value TEXT)")
        cursor.execute("INSERT INTO async_test VALUES (1, 'async_data')")
        
        # 查询数据
        cursor.execute("SELECT * FROM async_test")
        rows = cursor.fetchall()
        
        print(f"✓ 异步数据库操作完成，查询到 {len(rows)} 行数据")
        for row in rows:
            print(f"  - ID: {row['id']}, Value: {row['value']}")
    
    print("✓ 异步数据库连接已关闭")

def demonstrate_resource_monitoring() -> None:
    """演示资源监控"""
    print("\n======== 7. 资源监控 ========")
    
    print("✓ 初始活跃资源数量:", len(resource_manager.get_active_resources()))
    
    # 创建一些资源
    with ExitStack() as stack:
        stack.enter_context(DatabaseConnection())
        stack.enter_context(ThreadSafeLock("monitor_test"))
        stack.enter_context(temporary_file(content="monitoring test"))
        
        active_resources = resource_manager.get_active_resources()
        print(f"✓ 创建资源后活跃数量: {len(active_resources)}")
        
        for resource in active_resources:
            print(f"  - {resource.name} ({resource.resource_type.name})")
        
        time.sleep(0.1)  # 模拟使用资源
    
    final_resources = resource_manager.get_active_resources()
    print(f"✓ 清理后活跃资源数量: {len(final_resources)}")

async def run_async_tests() -> None:
    """运行异步测试"""
    await demonstrate_async_contexts()

def run_comprehensive_tests() -> None:
    """运行综合测试套件"""
    print("\n======== 8. 综合测试套件 ========")
    
    test_functions = [
        ("基础上下文管理器测试", demonstrate_basic_context_managers),
        ("数据库上下文管理器测试", demonstrate_database_context),
        ("线程安全测试", demonstrate_thread_safety),
        ("嵌套上下文测试", demonstrate_nested_contexts),
        ("异常处理测试", demonstrate_exception_handling),
        ("资源监控测试", demonstrate_resource_monitoring),
    ]
    
    passed = 0
    total = len(test_functions)
    
    for test_name, test_func in test_functions:
        try:
            test_func()
            print(f"✓ {test_name} 通过")
            passed += 1
        except Exception as e:
            print(f"✗ {test_name} 失败: {e}")
            logger.exception(f"测试失败: {test_name}")
    
    # 运行异步测试
    try:
        asyncio.run(run_async_tests())
        print(f"✓ 异步上下文管理器测试 通过")
        passed += 1
        total += 1
    except Exception as e:
        print(f"✗ 异步上下文管理器测试 失败: {e}")
        logger.exception("异步测试失败")
        total += 1
    
    # 最终清理
    resource_manager.cleanup_all()
    
    print(f"\n测试结果: {passed}/{total} 通过")
    print("所有企业级上下文管理器最佳实践演示完成!")

if __name__ == "__main__":
    run_comprehensive_tests()